package main

import (
	"fmt"
	mqtt "github.com/eclipse/paho.mqtt.golang"
	"runtime/debug"
	"strings"
	"time"
	"yhzn/exchange-cabinet-device/cabinet"
	web_socket "yhzn/exchange-cabinet-device/web-socket"
	"zym.com/tools/zconst"
	"zym.com/tools/zunique"

	"zym.com/tools"
	"zym.com/tools/zconvert/conv"
	"zym.com/tools/zconvert/json"
	"zym.com/tools/zfile"
)

// MqttHeart mqtt协议头
type MqttHeart struct {
	Rid      string `json:"rid"`              //
	DeviceId string `json:"device_id"`        //
	Ev       string `json:"ev"`               //
	Ct       int64  `json:"ct"`               //
	STime    string `json:"s_time,omitempty"` // 状态时间
}

// Heartbeat 心跳数据
type Heartbeat struct {
	T   byte    `json:"t"`   //
	Ta  byte    `json:"ta"`  //
	V   float32 `json:"v"`   //
	I   float32 `json:"i"`   //
	P   float32 `json:"p"`   //
	E   float32 `json:"e"`   //
	Pf  float32 `json:"pf"`  //
	AMD byte    `json:"amd"` //
	NetworkInfo
}

// DeviceStatus 充电分板状态
type DeviceStatus struct {
	No            byte   `json:"no"`             // 仓号
	Communication byte   `json:"communication"`  // 分板通迅
	Door          byte   `json:"door"`           // 门状态
	IsBattery     byte   `json:"is_battery"`     // 是否有电池
	OutFire       byte   `json:"out_fire"`       // 消防
	Urgency       byte   `json:"urgency"`        // 紧流停止
	T             byte   `json:"t"`              // 充电仓温度
	Battery       uint16 `json:"battery"`        // 电池电压
	ChargerOnline byte   `json:"charger_online"` // 充电器在线状态
	V             uint16 `json:"v"`              // 充电电压
	I             uint16 `json:"i"`              // 充电电流
	S             uint16 `json:"s"`              // 0:空闲 1:正在开机 2:充电中 3:充满电  4:限额充电
	Bid           string `json:"bid"`            // 电池编号
	Kwh           uint16 `json:"kwh"`            // 电池电量
	CellTemp      uint16 `json:"cell_temp"`      // 电芯温度
	BatteryStatus byte   `json:"battery_status"` // 电池健康状态 0：正常，1：故障
	SingleVol     string `json:"single_vol"`     // 电芯温度
}

// BmsStatus bms状态
type BmsStatus struct {
	No  byte     `json:"no"`  //
	Bid string   `json:"bid"` //
	V   uint16   `json:"v"`   //
	N   uint16   `json:"n"`   //
	Kwh uint16   `json:"kwh"` // 电池最量（SOC）
	Cap uint16   `json:"cap"` // 电池容量（AH）
	OI  uint16   `json:"oi"`  // 放电电流/电池健康度（在新的协议里为电池SOH）
	II  uint16   `json:"ii"`  // 充电电流
	T1  uint16   `json:"t1"`  // 环境温度
	T2  uint16   `json:"t2"`  // 电芯温度
	T3  uint16   `json:"t3"`  // 板卡温度
	Bv  []uint16 `json:"bv"`  // 各电芯电压
	Io  struct {
		Io1  byte `json:"io1"`  //
		Io2  byte `json:"io2"`  //
		Io3  byte `json:"io3"`  //
		Io4  byte `json:"io4"`  //
		Io5  byte `json:"io5"`  //
		Io6  byte `json:"io6"`  //
		Io7  byte `json:"io7"`  //
		Io8  byte `json:"io8"`  //
		Io9  byte `json:"io9"`  //
		Io10 byte `json:"io10"` //
		Io11 byte `json:"io11"` //
		Io12 byte `json:"io12"` //
	} `json:"io"`
	B1 []uint16 `json:"b1"` // 电芯过充保护
	B2 []uint16 `json:"b2"` // 电芯过放保护
}

// GeneralOrder 一般指令
type GeneralOrder struct {
	No        byte `json:"no"`        // 仓门号
	Operation byte `json:"operation"` // 操作数
}

type QRCode struct {
	Code string `json:"code"` // 设备二维码
}

type HelpPhone struct {
	Phone string `json:"phone"` // 帮助电话
}

type GetBmsOrder struct {
	No byte `json:"no"` // 仓门号
}

type Command struct {
	Cmds []string
}

//type SetDomainOrder struct {
//	ApiDomain  string `json:"api_domain"`  //
//	ApiPort    int    `json:"api_port"`    //
//	MqttDomain string `json:"mqtt_domain"` //
//	MqttPort   int    `json:"mqtt_port"`   //
//}

var cab cabinet.StatusInfo // 设备状态
var chanStatus = make(chan byte, 1)
var bms []BmsStatus
var AMDCommunication byte // 主板通信息
var devStatusTime string  // 最后成功读取设备状态的时间

// ReadStatus 读取设备状态(时钟)
func readStatusThread() {
	devStatusTimer = time.NewTicker(time.Duration(cfg.DevStatusInterval) * time.Second)
	defer func() {
		if p := recover(); p != nil {
			debug.PrintStack()
			writeLog(string(debug.Stack()))
		}
	}()
	for {
		select {
		case <-devStatusTimer.C:
			_ = readStatus()

			if cab.Lightning == 1 {
				if cardNo, _, _ := cabinet.ReadCardNo(); cardNo != 0 {
					web_socket.Hint("Read card success", 3)
					//web_socket.Hint("Read card success", 0)
					uploadCardNo(cardNo)
				}
			}
		}
	}
}

// 读取设备状态
func readStatus() (e error) {
	var j string
	var tmpCab cabinet.StatusInfo
	// 解析获取Bms信息
	//if bms == nil {
	//	bms = make([]BmsStatus, cfg.CabinetSum)
	//}
	//
	//for i := 0; i < int(cfg.CabinetSum); i++ {
	//	bms[i] = getBms(byte(i + 1))
	//}

	tmpCab, e = cabinet.ReadCabStatus()
	if e != nil {
		fmt.Println(e)
		AMDCommunication = 1
		return
	}
	devStatusTime = time.Now().Format(zconst.TIME_yyyyMMddHHmmss)
	//fmt.Println("状态读取成功")
	AMDCommunication = 0

	// 仓门或电池状态是否有变化
	for i, v := range tmpCab.Small {
		if i >= int(cfg.CabinetSum) {
			break
		}
		newBid := convBid(v.BmsId)
		oldBid := convBid(cab.Small[i].BmsId)
		changeStatus := 0
		if (newBid == "" && oldBid != "") || (oldBid == "" && newBid != "") {
			c := convDeviceStatus(byte(i+1), tmpCab)
			AddPublishList(topicPublishMessage, UpCmdChangeStatus, "", c, mqttClient.Cli)
			changeStatus |= 1
		}

		if v.Door != cab.Small[i].Door {
			if changeStatus&1 == 1 {
				time.Sleep(1 * time.Second)
			}
			c := convDeviceStatus(byte(i+1), tmpCab)
			AddPublishList(topicPublishMessage, UpCmdChangeStatus, "", c, mqttClient.Cli)
			if yhClient.Cli != nil {
				AddPublishList(topicPublishMessage, UpCmdChangeStatus, zunique.GetStrTwitterId()+"-yh", c, yhClient.Cli)
			}
		}
		ii := tmpCab.Small[i].ChargeI << 8 & 0xff00
		ii += tmpCab.Small[i].ChargeI >> 8 & 0x00ff
		tmpCab.Small[i].ChargeI = ii
	}
	// 报警信息是否有变化
	if tmpCab.MaintainDoor != cab.MaintainDoor ||
		//tmpCab.Lightning != cab.Lightning ||
		tmpCab.Fire != cab.Fire ||
		tmpCab.Smoke != cab.Smoke ||
		tmpCab.Water != cab.Water ||
		tmpCab.UrgencyStop != cab.UrgencyStop {
		AddPublishList(topicPublishMessage, UpCmdWarning, "", tmpCab.SensorStatus, mqttClient.Cli)
	}

	setStatus(tmpCab)

	j, e = json.ToJson(cab)
	if e == nil {
		_ = zfile.WriteFile(statusFile, []byte(j))
	}

	// 解析获取Bms信息
	if bms == nil {
		bms = make([]BmsStatus, cfg.CabinetSum)
	}

	for i := 0; i < int(cfg.CabinetSum); i++ {
		bms[i] = getBms(byte(i + 1))
	}

	return
}

// 复制设备状态对象
func copyStatus() (c cabinet.StatusInfo) {
	chanStatus <- 1
	c = cab
	<-chanStatus
	return
}

// 赋值设备状态对象
func setStatus(v cabinet.StatusInfo) {
	chanStatus <- 1
	cab = v
	<-chanStatus
}

// ====================================================================================================================

// 上传设备充电仓状态(时钟)
func uploadStatusThread() {
	uploadStatusTimer = time.NewTicker(time.Duration(cfg.UploadStatusInterval) * time.Second)
	defer func() {
		if p := recover(); p != nil {
			debug.PrintStack()
			writeLog(string(debug.Stack()))
		}
	}()
	for {
		select {
		case <-uploadStatusTimer.C:
			uploadStatus()
		}
	}
}

// 上传设备状态
func uploadStatus() {
	var s = make([]DeviceStatus, cfg.CabinetSum)
	for i := 0; i < int(cfg.CabinetSum); i++ {
		s[i] = getDeviceStatus(byte(i + 1))

	}
	AddPublishList(topicPublishMessage, UpCmdCabinetStatus, "", s, mqttClient.Cli)
}

// ============================================================================================

// 上传心跳(设备整机状态)(时钟)
func uploadHeartbeatThread() {
	heartbeatTimer = time.NewTicker(time.Duration(cfg.HeartbeatInterval) * time.Second)
	defer func() {
		if p := recover(); p != nil {
			debug.PrintStack()
			writeLog(string(debug.Stack()))
		}
	}()
	for {
		select {
		case <-heartbeatTimer.C:
			uploadHeartbeat(mqttClient.Cli)
		}
	}
}

// 上传心跳
func uploadHeartbeat(cl mqtt.Client) {
	var hb Heartbeat
	c := copyStatus()
	hb.T = c.Parameter.T
	hb.I = c.Parameter.I
	hb.V = c.Parameter.V
	hb.E = c.Parameter.E
	hb.P = c.Parameter.P * 1000.0
	hb.Pf = c.Parameter.Pf
	hb.Ta = c.Parameter.Ta
	hb.AMD = AMDCommunication
	hb.NetworkInfo = netInfo
	AddPublishList(topicPublishMessage, UpCmdHeartbeat, "", hb, cl)
	if yhClient.Cli != nil {
		AddPublishList(topicPublishMessage, UpCmdHeartbeat, zunique.GetStrTwitterId()+"-yh", c, yhClient.Cli)
	}
}

// 上传注册
func uploadRegister(cl mqtt.Client) {
	var hb Heartbeat
	c := copyStatus()
	hb.T = c.Parameter.T
	hb.I = c.Parameter.I
	hb.V = c.Parameter.V
	hb.E = c.Parameter.E
	hb.P = c.Parameter.P
	hb.Pf = c.Parameter.Pf
	hb.Ta = c.Parameter.Ta
	hb.NetworkInfo = netInfo
	AddPublishList(topicPublishMessage, UpCmdRegister, "", hb, cl)
}

func uploadCardNo(cardNo uint32) {
	o := struct {
		CardNumber uint32 `json:"cardnumber"`
	}{cardNo}

	AddPublishList(topicPublishMessage, "read_card", "", o, mqttClient.Cli)
}

// ===========================================================================================

// 发布bms信息
func publishBms(no byte, rid string, c mqtt.Client) {
	b := bms[no-1]

	AddPublishList(topicPublishResponse, DownCmdBms, rid, b, c)
	return
}

// 发布单个柜子状态信息
func publishCabinetStatus(no byte, rid string, c mqtt.Client) {
	b := getDeviceStatus(no)

	AddPublishList(topicPublishResponse, DownCmdGetCabStatus, rid, b, c)
}

// 获取BMS信息
func getBms(no byte) (bms BmsStatus) {
	if no <= 0 {
		return
	}
	defer func() {
		if p := recover(); p != nil {
			debug.PrintStack()
		}
	}()

	c := copyStatus()

	bms.No = no
	small := c.Small[no-1]

	bms.Bid = convBid(small.BmsId)
	size := len(bms.Bid)
	if bms.Bid == "" || size < 5 {
		return
	}

	i := 0
	_ = conv.BytesToObject(small.BmsAnalog[i:i+2], conv.ASC, &bms.V)
	i += 2
	_ = conv.BytesToObject(small.BmsAnalog[i:i+2], conv.ASC, &bms.N)
	i += 2
	_ = conv.BytesToObject(small.BmsAnalog[i:i+2], conv.ASC, &bms.Kwh)
	i += 2
	_ = conv.BytesToObject(small.BmsAnalog[i:i+2], conv.ASC, &bms.Cap)
	i += 2
	_ = conv.BytesToObject(small.BmsAnalog[i:i+2], conv.ASC, &bms.OI)
	i += 2
	_ = conv.BytesToObject(small.BmsAnalog[i:i+2], conv.ASC, &bms.II)
	i += 2
	_ = conv.BytesToObject(small.BmsAnalog[i:i+2], conv.ASC, &bms.T1)
	i += 2
	_ = conv.BytesToObject(small.BmsAnalog[i:i+2], conv.ASC, &bms.T2)
	i += 2
	_ = conv.BytesToObject(small.BmsAnalog[i:i+2], conv.ASC, &bms.T3)
	i += 2
	// 电芯电压
	bms.Bv = make([]uint16, bms.N)
	var x = 0
	for x = 0; x < int(bms.N); x++ {
		_ = conv.BytesToObject(small.BmsAnalog[i:i+2], conv.ASC, &bms.Bv[x])
		i += 2
	}
	value := 1
	bms.Io.Io1 = tools.IF(int(small.BmsIo[0])&value == value, byte(1), byte(0)).(byte)
	value = value << 1
	bms.Io.Io2 = tools.IF(int(small.BmsIo[0])&value == value, byte(1), byte(0)).(byte)
	value = value << 1
	bms.Io.Io3 = tools.IF(int(small.BmsIo[0])&value == value, byte(1), byte(0)).(byte)
	value = value << 1
	bms.Io.Io4 = tools.IF(int(small.BmsIo[0])&value == value, byte(1), byte(0)).(byte)
	value = value << 1
	bms.Io.Io5 = tools.IF(int(small.BmsIo[0])&value == value, byte(1), byte(0)).(byte)
	value = value << 1
	bms.Io.Io6 = tools.IF(int(small.BmsIo[0])&value == value, byte(1), byte(0)).(byte)
	value = value << 1
	bms.Io.Io7 = tools.IF(int(small.BmsIo[0])&value == value, byte(1), byte(0)).(byte)
	value = value << 1
	bms.Io.Io8 = tools.IF(int(small.BmsIo[0])&value == value, byte(1), byte(0)).(byte)

	value = 1
	bms.Io.Io9 = tools.IF(int(small.BmsIo[1])&value == value, byte(1), byte(0)).(byte)
	value = value << 1
	bms.Io.Io10 = tools.IF(int(small.BmsIo[1])&value == value, byte(1), byte(0)).(byte)
	value = value << 1
	bms.Io.Io11 = tools.IF(int(small.BmsIo[1])&value == value, byte(1), byte(0)).(byte)
	value = value << 1
	bms.Io.Io12 = tools.IF(int(small.BmsIo[1])&value == value, byte(1), byte(0)).(byte)
	value = value << 1

	bms.B1 = make([]uint16, bms.N)
	bms.B2 = make([]uint16, bms.N)

	j := 1 // 过充电压保护的起始数组位置
	for i = 0; i < int(bms.N); i++ {
		if value == 0x80 {
			bms.B1[i] = tools.IF(int(small.BmsIo[j])&value == value, uint16(1), uint16(0)).(uint16)
			value = 1
			j += 1
		} else {
			bms.B1[i] = tools.IF(int(small.BmsIo[j])&value == value, uint16(1), uint16(0)).(uint16)
			value = value << 1
		}
	}
	j = 4 // 过放电压保护的起始数组位置
	value = 1
	for i = 0; i < int(bms.N); i++ {
		if value == 0x80 {
			bms.B2[i] = tools.IF(int(small.BmsIo[j])&value == value, uint16(1), uint16(0)).(uint16)
			value = 1
			j += 1
		} else {
			bms.B2[i] = tools.IF(int(small.BmsIo[j])&value == value, uint16(1), uint16(0)).(uint16)
			value = value << 1
		}
	}
	return
}

// 获取设备状态
func getDeviceStatus(no byte) (status DeviceStatus) {
	i := no - 1
	if i >= 32 {
		return
	}
	c := copyStatus()
	status.No = no
	status.Communication = c.Small[i].Online
	status.Door = c.Small[i].Door
	status.IsBattery = c.Small[i].BatteryP
	status.OutFire = c.Small[i].OutFire
	status.Urgency = c.Small[i].Urgency
	if bms != nil {
		status.Battery = bms[i].V //c.Small[i].BatteryV
		status.CellTemp = bms[i].T1
		status.BatteryStatus = batteryIsFault(bms[i])
		for _, v := range bms[i].Bv {
			status.SingleVol += fmt.Sprintf("%0.2f,", float32(v)/1000.0)
		}
		if status.SingleVol != "" {
			status.SingleVol = status.SingleVol[:len(status.SingleVol)-1]
		}

	}

	status.ChargerOnline = 1 //c.Small[i].ChargeOnline
	status.V = c.Small[i].ChargeV
	status.I = c.Small[i].ChargeI
	status.S = c.Small[i].ChargerStatus
	status.T = c.Small[i].Tem

	status.Bid = convBid(c.Small[i].BmsId)
	// 因为设备没有接检测是否电池的线，所以这里用有无电池来判断是否是电池
	if status.Bid == "" {
		status.IsBattery = 0
	} else {
		status.IsBattery = 1
	}

	// 电池连接，且电流为0，认为是充满状态 状态转化为3
	if status.Bid != "" && status.I == 0 {
		status.S = 3
	}
	status.Kwh = uint16(c.Small[i].BmsAnalog[5])
	return
}

func batteryIsFault(bms BmsStatus) byte {
	if bms.Io.Io1 == 0x01 || bms.Io.Io2 == 0x01 || bms.Io.Io3 == 0x01 ||
		bms.Io.Io4 == 0x01 || bms.Io.Io5 == 0x01 || bms.Io.Io6 == 0x01 ||
		bms.Io.Io7 == 0x01 || bms.Io.Io8 == 0x01 || bms.Io.Io9 == 0x01 ||
		bms.Io.Io10 == 0x01 || bms.Io.Io11 == 0x01 || bms.Io.Io12 == 0x01 {
		return 1
	}
	return 0
}

// 读取的设备状态转为上传的结构
func convDeviceStatus(no byte, info cabinet.StatusInfo) (status DeviceStatus) {
	i := no - 1
	if i >= 32 {
		return
	}
	c := info
	status.No = no
	status.Communication = c.Small[i].Online
	status.Door = c.Small[i].Door
	status.IsBattery = c.Small[i].BatteryP
	status.OutFire = c.Small[i].OutFire
	status.Urgency = c.Small[i].Urgency
	if bms != nil {
		status.Battery = bms[i].V // c.Small[i].BatteryV
		status.CellTemp = bms[i].T1

		status.BatteryStatus = batteryIsFault(bms[i])
		for _, v := range bms[i].Bv {
			status.SingleVol += fmt.Sprintf("%0.2f,", float32(v)/1000.0)
		}
		if status.SingleVol != "" {
			status.SingleVol = status.SingleVol[:len(status.SingleVol)-1]
		}
	}

	status.ChargerOnline = c.Small[i].ChargeOnline
	status.V = c.Small[i].ChargeV
	status.I = c.Small[i].ChargeI
	status.S = c.Small[i].ChargerStatus
	status.Bid = convBid(c.Small[i].BmsId)
	status.T = c.Small[i].Tem
	// 因为设备没有接检测是否电池的线，所以这里用有无电池来判断是否是电池
	if status.Bid == "" {
		status.IsBattery = 0
	} else {
		status.IsBattery = 1
	}
	// 电池连接，且电流为0，认为是充满状态 状态转化为3
	if status.Bid != "" && status.I == 0 {
		status.S = 3
	}
	status.Kwh = uint16(c.Small[i].BmsAnalog[5])
	return
}

// 处理电池ID
func convBid(bId [26]byte) string {
	j := 0
	for j = len(bId) - 1; j >= 0; j-- {
		if bId[j] != 0x00 {
			break
		}
	}
	bid := fmt.Sprintf("%s", bId[0:j+1])
	bid = strings.TrimSpace(bid)
	return bid
}
